#pragma once

#include <algorithm>
#include "rpc/RPCActor.h"

template <
class HANDLER_TYPE, class OBJECT_TYPE, std::size_t MESSAGE_COUNT, typename... Args>
class MessageHandler
{
public:
    MessageHandler() {
        std::fill_n(handlers_, MESSAGE_COUNT, nullptr);
        std::fill_n(rpc_handlers_, MESSAGE_COUNT, nullptr);
    }

    bool CanHandle(uint32 opcode) const {
        if (opcode < MESSAGE_COUNT) {
            return handlers_[opcode] != nullptr || rpc_handlers_[opcode] != nullptr;
        } else {
            return false;
        }
    }

    void InitHandlerFlags(bool flags[], std::size_t size) const {
        for (std::size_t i = 0, n = std::min(MESSAGE_COUNT, size); i < n; ++i) {
            flags[i] = handlers_[i] != nullptr || rpc_handlers_[i] != nullptr;
        }
    }

    void MergeHandlerFlags(bool flags[], std::size_t size) const {
        for (std::size_t i = 0, n = std::min(MESSAGE_COUNT, size); i < n; ++i) {
            flags[i] |= handlers_[i] != nullptr || rpc_handlers_[i] != nullptr;
        }
    }

    int HandlePacket(OBJECT_TYPE *obj, INetPacket &pck, Args... args) {
        auto opcode = pck.GetOpcode();
        if (opcode < MESSAGE_COUNT) {
            if (handlers_[opcode] != nullptr) {
                return (((HANDLER_TYPE*)this)->*handlers_[opcode])(obj, pck, args...);
            } else if (rpc_handlers_[opcode] != nullptr) {
                return (((HANDLER_TYPE*)this)->*rpc_handlers_[opcode])
                    (obj, pck, RPCActor::ReadRequestMetaInfo(pck), args...);
            }
        }
        return SessionHandleUnhandle;
    }

protected:
    typedef int (HANDLER_TYPE::*HandlerType)(OBJECT_TYPE*, INetPacket&, Args...);
    typedef int (HANDLER_TYPE::*RPCHandlerType)(OBJECT_TYPE*, INetPacket&,
        const RPCActor::RequestMetaInfo&, Args...);

    HandlerType handlers_[MESSAGE_COUNT];
    RPCHandlerType rpc_handlers_[MESSAGE_COUNT];
};
